\documentclass[a4paper,10pt]{article}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage[utf8]{inputenc}
\usepackage[spanish]{babel}
\usepackage[pdftex]{graphicx}

\begin{document}
\title{\textbf{Sistemas Operativos}\\ Trabajo Práctico 1\\ }
\author{45129 - Gou Xinwei \\ 45390 - Oybin Pablo Nahuel \\ 46069 - Sessa Carlos}
\date{}
\maketitle

\begin{abstract}
\begin{center}
    Filesystems, IPCs y Servidores Concurrentes.
\end{center}
\end{abstract}

\section{Objetivos}
El objetivo de este trabajo es familiarizarse con el uso de sistemas cliente-servidor
concurrentes, implementando el servidor mediante la creación de procesos hijos
utilizando fork() y mediante la creación de threads. Al mismo tiempo, ejercitar el
uso de los distintos tipos de primitivas de sincronización y comunicación de
procesos (IPC) y manejar con autoridad el filesystem de Linux desde el lado
del usuario.

\section{Consigna}
\begin{enumerate}
 \item Implementar un servidor concurrente creando procesos hijos mediante fork() y
    hacer cuatro versiones del sistema, usando las siguientes primitivas de IPC:
        \begin{itemize}
            \item Pipes o FIFOs.
            \item Colas de mensajes - System V o POSIX.
            \item Memoria compartida - System V o mmap(). Semáforos - System V o  POSIX.
            \item Sockets - TCP o de dominio Unix.
        \end{itemize}
    \item Implementar una quinta versión funcionalmente equivalente a las anteriores,
    utilizando threads, mutexes y variables de condición (pthreads).
\end{enumerate}

\section{Introducción}
En este escrito se explicarán las decisiones tomadas durante el desarrollo del
trabajo. En la primera sección \textbf{Herramientas} se da a conocer qué
se usó aparte del código que corresponde a los IPCs. Se analizan procedimientos
comunes en \textbf{Funcionamiento común}. En
\textbf{Implementación con primitivas de IPC} se explica qué se hizo para
realizar el programa utilizando fork() para luego dar su explicación con
threads en \textbf{Implementación con pthreads}. Por último se ofrece
una sección de \textbf{Problemas encontrados} y \textbf{Conclusiones}.

\section{Herramientas}
\subsection{Base de Datos}
En un principio pensamos hacer manejo de información con archivos utilizando
fcntl pero notamos que una mejor solución sería una base de datos.
No tiene sentido usar una base de datos cliente-servidor como ser MySQL
\footnote{http://www.mysql.com} entonces optamos por SQLite
\footnote{http://www.sqlite.org}.
SQLite ofrece una API extensa, pero no lo suficientemente cómoda para utilizar
directamente. Entonces empezamos a armar un ADT de manejo de base de datos que
sea \textit{thread-safe} y nos abstraiga del API de SQLite.
Ya que muchos trabajos de la facultad requieren manejo de información,
decidimos liberar el código y ofrecerlo en un repositorio público
\footnote{http://code.google.com/p/database-adt/} para que cualquiera pueda
utilizarlo y mejorarlo. Definitivamente fue una decisión acertada porque,
gracias a liberarlo, recibimos comentarios de mejoras y notificación de
memory leaks que no habíamos notado.

\subsection{Inotify}
Para hacer detección de cambios en el filesystem sabíamos que hacer polling no era
la solución. Viendo cómo resolvía boxbackup esta problemática descubrimos el
subsistema del Kernel de Linux llamado inotify
\footnote{http://www.boxbackup.org/trac/wiki/Inotify}.
Probando un par de ejemplos notamos que cuando detectábamos cambios en los
directorios inotify no lo hacía de manera recursiva. Buscando por internet
encontramos inotify-tools \footnote{http://inotify-tools.sourceforge.net/}, 
una librería qué ofrece una interfaz más simple para inotify y tiene en cuenta
la funcionalidad recursiva. Lamentablemente la cátedra no permitió usarla
entonces hicimos un notifyADT que nos ofrezca lo necesario.

\subsection{Getopt}
Para parsear los comandos que se le pasan a la aplicación se usa getopt.
Tanto el cliente como el servidor usan el mismo set de parámetros. El
proceso principal recibe qué IPC usar y de qué manera quiere realizar
trabajos en paralelo. Si los parámetros fueron pasados correctamente
se llaman a las funciones correspondientes y si no se muestra el help
del programa, como cualquier otra aplicación de UNIX.

\subsection{Split}
Para manejar los forks y/o hijos de la aplicación se diseño una librería
que nos abstraiga de qué manera se realizaba. Por ejemplo ya no existen
fork() y pthread\_create() dentro del código, fueron reemplazados por
una función de esta librería.

\subsection{Estructuras de Datos}
La mayoría de las estructuras de datos utilizadas en este trabajo fueron
programadas con anterioridad en otras materias.

\section{Funcionamiento común}
A continuación se mencionan puntos en común a todas las implementaciones
de IPC del cliente y del servidor:

\subsection{Cliente}
\begin{itemize}
    \item La función \textit{readCommands} se ocupa de parsear los comandos
y ejecutarlos.

    \item El cliente es concurrente. El usuario puede escribir comandos
mientras recibe updates del server.

    \item La recepción y envio de archivos se realizan por medio de buffers.

    \item En caso de un conflicto aparecerá un .mine en el directorio
sincronizado y no habrá commits hasta que se solucione.
\end{itemize}

\subsection{Servidor}
\begin{itemize}
    \item La función \textit{readCommands} se ocupa de parsear los comandos.

    \item La actualización o creación de un archivo se recibe en un archivo
.new. En el caso que otro cliente quiera hacer update de ese mismo archivo
recibirá un mensaje avisándole que primero recibirá un update y deberá
arreglar un conflicto.

    \item El uso de la base de datos asegura que no puedan conectarse dos
usuarios con nombres iguales.

    \item Los usuarios sólo pueden sincronizar directorios que figuren
en la base de datos.


\end{itemize}

\section{Implementación con primitivas de IPC}
\subsection{FIFOs}
Para este caso el servidor tiene un fifo público el cual todos conocen.
El cliente envía comandos a ese fifo. Cuando el servidor recibe una conexión, 
éste crea un proceso hijo para atenderla y poder seguir escuchando por el fifo
público.
En el caso que la operación requiera recepción de archivos y/o comandos por
parte del servidor, este crea un fifo privado para escuchar las respuestas
del cliente y le envía el nombre.
Cada vez que se realiza una operación, se terminan los procesos hijos en el
servidor que atiendían esa operación.
Los nombres de los FIFOs de los clientes es una concatenación de fifo
con un entero obtenido a partir de getpid().
Y los nombres de los FIFOs privados del servidor son una concatencación del
nombre del FIFO privado del cliente con .serv. De esta forma evitamos
nombres duplicados.
Además para poder hacer el reenvío de los cambios a todos los clientes,
guardamos en la base de datos no sólo el nombre de sesión del cliente,
sino también el nombre de su fifo.
Los fifos los creamos en la carpeta /tmp y son elminados luego de que el
cliente cierre la sesión, o el servidor termine la ejecución.


\subsection{Colas de mensaje}
La implementación utilizando Colas de mensajes es similar a la implementación
utilizando pipes (FIFOs). En este caso se crea una única cola de mensajes.
El ID público del servidor es conocido por el cliente de ante mano así como
también es conocido por ambos el ID de la cola de mensajes. Cada cliente
obtiene además una identificación mediante la función getpid() (de esta
forma nos aseguramos que no haya ID duplicados. En el momento de loguearse,
el cliente envía el nombre de sesión así como también este ID, valores que son
almacenados en la base de datos. Por otro lado, el servidor cuando atiende
cada conexión (mediante un hijo distinto) obtiene un ID privado para cada
conexión. Este ID lo obtiene multiplicando el ID del cliente por una constante.
Al igual que en FIFOs, tras cada operación se termina el proceso hijo en el
servidor que la atendía.

\subsection{Memoria compartida}
Para la implementaci\'on de memoria compartida se tomo el dise\~no de FIFO, donde
en vez de tener un FIFO p\'ublico, se tiene ahora un segmento de memoria
f\'isica para la comunicaci\'on entre los clientes con el servidor. El segmento de
memoria p\'ublica es conocido tanto por servidor como por los clientes ya que se
inicia con un ID conocido por ambas partes. An\'alogamente a la implementaci\'on
de FIFO, para las operaciones y transmisiones de archivos, el cliente crea un
segmento de memoria con ID que lo identifica un\'ivocamente (en este caso se usa la 
funci\'on getpid()), \'este le pasa la informaci\'on necesaria (una estructura shmCDT
donde se guarda el id, un char* que apunta al segmento de la memoria a compartir y un
identificador del semaforo) a trav\'es del segmento de memo p\'ublica. El manejo de
procesos es idem al de FIFOs. 

\subsection{Sockets}
Para la implementación con sockets, utilizamos comunicación orientada a
conexión (TCP).
En este caso a diferencia de las implementaciones con los otros IPCs,
las conexiones entre cliente y servidor no se cierran hasta que el cliente
cierra la sesión o se terminan tanto la aplicación cliente o bien la
aplicación servidor. El cliente se conecta al socket público del servidor a
la dirección "localhost", en un puerto establecido previamente. Comose explicó
en otra sección, el servidor crea un proceso que se encarga de escuchar en una
cola de mensajes. Así cuando recibe un cambio en alguno de los clientes, este
proceso mediante una cola de mensajes, le avisa al resto de los hijos del
servidor, quienes mantienen las conexiones con los clientes, que envíen los
correspondientes cambios. El ID de cada cliente para diferenciarlos en la cola
de mensajes, es indicado por el servidor cuando el cliente se loguea (ejecuta
el comando "nombre").

\section{Implementación con pthreads}
Habiendo dejado este punto para el final y después de hacer pruebas con la
librería pthreads descubrimos que lo único que teníamos que hacer es modificar
las llamadas a fork y reemplazarlas por una función que determine si queremos
forkear o crear un thread.

Una ventaja de nuestra implementación es que las variables globales que
existen se inicializan en el main y luego sólo se leen. Esto asegura que
no necesitemos variables mutex para usar nuestro programa con threads
ya que no hay race conditions.

\section{Problemas encontrados}

\subsection{Limitando la cantidad de clientes}
Probando usando el sistema con muchos clientes, notamos que la performance
era muy mala. Decidimos que lo mejor sería limitar la cantidad de clientes
que se podían conectar. Para lograr eso aprovechamos la tabla \textbf{Online}
dentro de la base de datos que sólo contenía los nombre de usuario. Al iniciar
el servidor, se borra todos los campos de la tabla y se crean $x$ cantidad de
slots disponibles dados por una constante. Si un cliente desea conectarse y
no hay slots disponibles, se muestra en el servidor un mensaje acorde.

\subsection{Cerrando la aplicación correctamente}
Haciendo testeos, encontramos que si se cerraba, abruptamente o no, el servidor
mientras que hacia una transferencia, el archivo quedaba corrupto. Como
nuestra aplicación es la misma para forks y para threads, debíamos guardar
los IDs de los procesos o la variable pthread según corresponda. Relacionando
un vector global con la tabla \textbf{Online} logramos el cometido.

Cuando el cliente o el servidor recibe el comando salir, o se le manda la señal
SIGINT, se ejecuta una función de salida. En esa función se realiza lo
siguiente:
\begin{itemize}
 \item Se mandan señales SIGUSR1 a los procesos/threads que tienen ciclos
infinitos para detenerlos.
 \item Le pide a la base de datos todos los índices del vector global que
están en uso y espera a que terminen.
\end{itemize}

De esta manera logramos que tanto cliente como servidor se cierren
correctamente sin dejar procesos zombies ni huérfanos.

\subsection{Incorrecta modularización por capas}
Cuando empezamos a realizar el trabajo nos enfocamos pura y exclusivamente
en pipes sin respetar la modolarización por capas. Una vez terminado el resto
de los IPCs notamos que teníamos mucha repetición de código.

\subsection{Inotify en otros sistemas operativos}
Lamentablemente inotify no es multiplataforma, pero descubrimos la existencia
de un proyecto denominado pnotify (http://code.google.com/p/pnotify/).
Pnotify es una librería que prentende obtener notificaciones de cambios en el
filesystem de manera portable.
Creemos que en el casoque alguien quiera utilizar el proyecto en otros sistemas
operativos, podría modificar el código de inotify reemplazándolo por pnotify para
su correcto funcionamiento.

\section{Posibles extensiones}
\subsubsection{Comando status}
Dada la información guardada en la tabla \textbf{Online} se podría hacer una
consulta de manera que se lista el estado de todas las conexiones.

\subsubsection{Login y sync de directorios según permisos}
Con una base de datos funcionando, lograr el login no es difícil. Luego con
una tabla de permisos determinar si el usuario puede o no puede sincronizar
una carpeta.

\subsubsection{Chat con usuarios conectados}
Ya teniendo armado toda la comunicación el chat sería tan fácil de hacer
como dejar un nombre de archivo reservado donde se escriben y leen los
mensajes.

\section{Conclusiones y observaciones}
\begin{itemize}
    \item Hacer una librería que maneje genéricamente tanto forks como threads
nos trajo más problemas que beneficios. Creemos que hubiera sido preferible
hacer dos ejecutables.
    \item La modularización por capas disminuye la repetición de código.
    \item El código en el handler de las señales debe ser lo más excueto posible.
\end{itemize}

\begin{thebibliography}{}

\bibitem[Keith Haviland, 1999]
- Keith Haviland, Dina Gray, Ben Salama, \emph{UNIX System Programming Second Edition},
Addison-Wesley, 1999.

\bibitem[Blaise Barney, 2009]
- Blaise Barney, \emph{POSIX Threads Programming},
https://computing.llnl.gov/tutorials/pthreads/,
Lawrence Livermore National Laboratory, 2009.



\end{thebibliography}
\end{document}
